use crate::qtrade::strategy::util::highlow_klines::compute_ohlcv_averages;
use crate::qtrade::strategy::util::highlow_klines::find_highest_klines;
use crate::qtrade::strategy::util::highlow_klines::find_lowest_klines;
use crate::todo::MAX_KLINE_DAYS;

use super::util::highlow_klines::calculate_kline_low_deviation;
use super::util::stats::calculate_upstops_count;
use anyhow::Result;
use anyhow::anyhow;

use chrono::Local;
use fudata::db::kline;
use fudata::db::malines::lsma::Lsma;
use fudata::db::malines::sma::Sma;
use fudata::db;
use fudata::db::malines::smpr::Smpr;
use fudata::model::kline::KlineStat;
use fudata::model::kline::Klt;
use fudata::model::kline::Kline;
use fudata::model::malines::ma::LSMAv;
use fudata::model::malines::ma::MAConfig;
use fudata::model::malines::ma::PeriodV;
use fudata::model::malines::ma::MA;

use fudata::model::security::Security;
use fudata::model::trade::trade_order::TradeOrder;
use fudata::model::trade::trade_order_data::Buy;
use fudata::model::trade::trade_order_data::PricingType;
use fudata::model::trade::trade_order_data::Sell;
use fudata::model::trade::trade_order_data::TradeOrderData;
use fudata::model::trade::trade_storder::UpstopReturnBuy;
use fudata::model::trade::trade_storder::ValleyReboundBuy;
use fudata::round2;
use log::info;
use log::trace;
use surrealdb::sql::Thing;
use yata::methods::CrossAbove;
use yata::core::Method;
use yata::methods::CrossUnder;
use std::str::FromStr;

use regex::Regex;

/// 涨停回落策略
///
/// 如果股价回落至涨停日K线的开盘价、前一日收盘价的小者，买入，
///
/// 而该涨停的开盘价或者前一日收盘价不能大于sma21太多，可设为5%
///
pub struct UpstopReturnStrategy {}

pub struct UpstopReturnStrategyBuyWorker {
    security: Security,
    /// all klines for finding lowest prices as basic info
    all_klines: Vec<Kline>,

    /// simple moving average of close price
    sma: Sma,

    /// 放量倍数
    smpr: Smpr,

    /// 涨跌停限制
    upstop_ratio: f64,
}

impl UpstopReturnStrategyBuyWorker {
    pub async fn new(security_id: &str, klt: &Klt) -> Result<Self> {
        let start_time = Local::now();

        let security = db::security::find(security_id).await?;
        let mut all_klines = db::kline::select(
            security_id,
            None,
            Some(klt),
            Some("klts DESC"),
            0,
            MAX_KLINE_DAYS as u16
        ).await?;
        all_klines.reverse();

        let re0 = Regex::new(r"^SZ.STK.0")?;
        let re3 = Regex::new(r"^SZ.STK.3")?;
        let re6 = Regex::new(r"^SH.STK.6")?;

        let upstop_ratio = if re0.is_match(security_id) || re6.is_match(security_id) {
            1.1
        } else if re3.is_match(security_id) {
            1.2
        } else {
            1.0
        };

        let rs = if let Some(fstkl) = all_klines.first() {
            let sma: Sma = Sma::new(&(MAConfig { periods: vec![5, 21] }), &fstkl.close)?;
            let smpr: Smpr = Smpr::new(
                &(MAConfig { periods: vec![5, 21, 120, 250] }),
                &(fstkl.volume as f64)
            )?;

            let spent_time = Local::now().signed_duration_since(start_time);
            trace!(
                "(new)  spent seconds={}.{} ",
                spent_time.num_seconds(),
                spent_time.num_milliseconds() % 1000
            );

            Ok(UpstopReturnStrategyBuyWorker {
                security,
                all_klines,
                sma,
                smpr,
                upstop_ratio,
            })
        } else {
            Err(anyhow!("not enough kline day data for {}", security_id))
        };
        rs
    }

    pub fn compute_stbuys(&mut self) -> Vec<ValleyReboundBuy> {
        let mut result: Vec<ValleyReboundBuy> = Vec::new();

        if self.all_klines.len() > 100 {
            let mut prev_kline: Option<Kline> = None;

            for (idx, kline) in self.all_klines.iter().enumerate() {
                if let Some(prev_kl) = prev_kline {
                    // close price
                    let the_close = kline.close;
                    let sma_v: MA<f64> = self.sma.next(&the_close);
                    let sma_v5 = sma_v.mav(5).unwrap_or_else(|| 0.0);
                    let sma_v21 = sma_v.mav(21).unwrap_or_else(|| 0.0);

                    if
                        is_upstop(round2(prev_kl.close * self.upstop_ratio), round2(kline.close)) &&
                        // 前一低价不能超过sma_v21的5%
                        prev_kl.ohlcv().l / sma_v21 < 1.05
                    {
                        // 收盘价60分钟5日均线上穿21日均线，买入 cross_above.next(&(sma_v5, sma_v21)).analog() == 1 &&
                        let bfbuy = UpstopReturnBuy {
                            prev_ohlcv: prev_kl.ohlcv(),
                            curr_ohlcv: kline.ohlcv(),
                        };

                        // let kline_id = kline.id.clone().unwrap().id.to_raw();
                        // let stbuy_id = TradeStValleyReboundBuy::id_from(kline_id.as_str());
                        // let stbuy = TradeStValleyReboundBuy {
                        //     // id: Some(Thing::from(("trade_stbuy", stbuy_id.as_str()))),
                        //     // security_id: self.security.id_raw(),
                        //     // kline_id,
                        //     // klts: kline.klts,
                        //     data: Some(TradeStbuyData::UpstopReturnBuy(bfbuy)),
                        //     is_valid: true,
                        //     invalid_by_kline_id: None,
                        //     invalid_message: None,
                        // };
                        // result.push(stbuy);
                    }
                }

                prev_kline = Some(kline.clone());
            }
        }
        print!(".");
        result
    }

    /// compute trade buy/sell orders based on a trade_stbuy until the trade_stbuy invalid
    pub fn compute_trade_orders(&self, stbuy: &mut ValleyReboundBuy) -> Vec<TradeOrder> {
        let mut torders: Vec<TradeOrder> = Vec::new();

        // match &stbuy.data {
        //     Some(TradeStbuyData::UpstopReturnBuy(urbuy)) => {
        //         // Process upstop return buy data

        //         // 找出stbuy中curr_ohlcv.o和prev_ohlcv.c之间小者
        //         let rprice = f64::min(
        //             f64::min(urbuy.prev_ohlcv.c, urbuy.prev_ohlcv.o),
        //             urbuy.curr_ohlcv.o
        //         );
        //         let rprice = f64::min(urbuy.prev_ohlcv.l, urbuy.curr_ohlcv.l);

        //         println!("rprice= {}", rprice);

        //         let klines: Vec<&Kline> = self.all_klines
        //             .iter()
        //             .filter(|kl| kl.klts > stbuy.klts)
        //             .collect();

        //         for (idx, kline) in klines.iter().enumerate() {
        //             if stbuy.is_valid == false {
        //                 break;
        //             }

        //             let kline_id = kline.id.clone().unwrap().id.to_raw();
        //             let ohlcv = kline.ohlcv();

        //             let is_last_trade_order_buy = match torders.last() {
        //                 // 判断是否已经买入
        //                 Some(TradeOrder { data: Some(TradeOrderData::Buy(buy)), .. }) => true,
        //                 _ => false,
        //             };
        //             // 回撤到涨停启动点买入并且没有最新买单
        //             if ohlcv.l < rprice && !is_last_trade_order_buy {
        //                 let trade_order = TradeOrder {
        //                     id: Some(Thing::from(("trade_order", kline_id.as_str()))),
        //                     kline_id: kline_id.to_owned(),
        //                     klts: Some(kline.klts.clone()),
        //                     security_id: Some(self.security.id_raw()),
        //                     strategry_id: Some("UpstopReturnStrategy".to_string()),
        //                     data: Some(
        //                         TradeOrderData::Buy(Buy {
        //                             security_name: self.security.id_raw(),
        //                             security_code: self.security.id_raw(),
        //                             pricing_type: PricingType::Limit,
        //                             buy_price: rprice,
        //                             volume: 100,
        //                             desc: "回撤到涨停启动点买入".to_owned(),
        //                             is_auto: false,
        //                             spl_price: round2(rprice * 0.99),
        //                         })
        //                     ),
        //                     ..Default::default()
        //                 };
        //                 torders.push(trade_order);
        //             }

        //             // 检查最新的买单是否需要卖出并标记stbuy是否继续有效
        //             let tso = match torders.last_mut() {
        //                 // 判断是否已经买入
        //                 Some(tod) => {
        //                     match &mut tod.data {
        //                         Some(TradeOrderData::Buy(buy)) => {
        //                             // Check if the difference is at least one day for T+1且小于之询价
        //                             if
        //                                 kline.klts
        //                                     .signed_duration_since(tod.klts.unwrap())
        //                                     .num_days() >= 1 &&
        //                                 (kline.open < buy.spl_price || kline.low < buy.spl_price)
        //                             {
        //                                 let tso = TradeOrder {
        //                                     id: Some(
        //                                         Thing::from(("trade_order", kline_id.as_str()))
        //                                     ),
        //                                     kline_id: kline_id.to_owned(),
        //                                     security_id: Some(self.security.id_raw()),
        //                                     strategry_id: Some("UpstopReturnStrategy".to_string()),
        //                                     data: Some(
        //                                         TradeOrderData::Sell(Sell {
        //                                             security_name: self.security.id_raw(),
        //                                             security_code: self.security.id_raw(),
        //                                             pricing_type: PricingType::Limit,
        //                                             price: f64::min(buy.spl_price, kline.open),
        //                                             volume: 100,
        //                                             desc: "回撤到涨停启动点买入".to_owned(),
        //                                             is_auto: false,
        //                                         })
        //                                     ),
        //                                     ..Default::default()
        //                                 };

        //                                 // 跌破涨停启动点设置buy无效了
        //                                 if kline.low < round2(rprice * 0.99) {
        //                                     //0.98
        //                                     stbuy.is_valid = false;
        //                                     stbuy.invalid_by_kline_id = Some(kline_id.to_owned());
        //                                     stbuy.invalid_message = Some(
        //                                         format!(
        //                                             "kline.low:{} < round2(rprice:{rprice}*0.99)",
        //                                             kline.low
        //                                         )
        //                                     );
        //                                 }

        //                                 Some(tso)
        //                             } else if kline.close > buy.spl_price {
        //                                 // 上浮止损价
        //                                 buy.spl_price = round2(kline.close * 0.99);
        //                                 None
        //                             } else {
        //                                 None
        //                             }
        //                         }
        //                         _ => None,
        //                     }
        //                 }
        //                 _ => None,
        //             };

        //             if let Some(t) = tso {
        //                 torders.push(t);
        //             }
        //         }
        //     }
        //     _ => {
        //         println!("IGNORE@{:?} {:?}", stbuy.id, stbuy.data);
        //     }
        // }

        torders
    }

    /// compute klines summary
    pub fn compute_klines_summary(
        &mut self,
        span_days: &Vec<i64>,
        compute_latest_days: usize
    ) -> Vec<KlineStat> {
        let start_time = Local::now();

        let mut klsums = Vec::new();
        for (idx, kline) in self.all_klines.iter().enumerate() {
            // refresh indicators computer
            let smpr_v: MA<f64> = self.smpr.next(&(kline.volume as f64));
            let sma_v: MA<f64> = self.sma.next(&(kline.close as f64));

            if
                self.all_klines.len() > compute_latest_days &&
                idx < self.all_klines.len() - compute_latest_days
            {
                // skip the previous klines for computing
                continue;
            }

            let kline_id = kline.id_raw();
            let idv = KlineStat::id_from(kline_id.as_str());
            let mut avg_ohlcv = MA { ..Default::default() };
            let mut highest = MA { ..Default::default() };
            let mut lowest = MA { ..Default::default() };
            let mut upstop_counts = MA { ..Default::default() };

            // for span_day in span_days {
            //     let upstop_count = calculate_upstops_count(&self.all_klines, &kline.klts, span_day);
            //     upstop_counts.values.push(PeriodV {
            //         period: span_day.1 as u16,
            //         value: upstop_count,
            //     });
            //     let highest_kls = find_highest_klines(&kline.klts, &self.all_klines, span_day, 1);
            //     if let Some(v) = highest_kls.first() {
            //         highest.values.push(PeriodV {
            //             period: span_day.1 as u16,
            //             value: v.klv(),
            //         });
            //     }

            //     let lowest_kls = find_lowest_klines(&kline.klts, &self.all_klines, span_day, 1);
            //     if let Some(v) = lowest_kls.first() {
            //         lowest.values.push(PeriodV {
            //             period: span_day.1 as u16,
            //             value: v.klv(),
            //         });
            //     }

            //     let ohlcv_kl = compute_ohlcv_averages(&kline.klts, &self.all_klines, span_day);
            //     if let Some(v) = ohlcv_kl {
            //         avg_ohlcv.values.push(PeriodV {
            //             period: span_day.1 as u16,
            //             value: v,
            //         });
            //     }
            // }

            let klsum = KlineStat {
                id: Some(Thing::from(("kline_summary", idv.as_str()))),
                kline_id,
                klts: kline.klts,
                security_id: self.security.id_raw(),
                market: self.security.market.clone(),
                security_type: self.security.type_.clone(),
                ohlcv: kline.ohlcv(),
                smpr: smpr_v,
                upstop_counts,
                avg_ohlcv,
                highest,
                lowest,
                ..Default::default()
            };

            klsums.push(klsum);
        }

        let spent_time = Local::now().signed_duration_since(start_time);
        trace!(
            "(compute_klines_summary)  spent seconds={}.{} ",
            spent_time.num_seconds(),
            spent_time.num_milliseconds() % 1000
        );

        klsums
    }
}

fn is_upstop(a: f64, b: f64) -> bool {
    // let epsilon = 1e-9; // Commonly used epsilon value
    (a - b).abs() < 1e-9
}

#[cfg(test)]
mod test {
    use std::io::Write;

    use fudata::db;
    use fudata::model::kline::Klt;
    use fudata::model::security;
    use fudata::model::security::Market;
    use fudata::model::security::Security;
    use fudata::model::trade::trade_order_data::TradeOrderData;
    use fudata::model::trade::trade_storder::ValleyReboundBuy;
    use fudata::round2;
    use log::error;
    use log::info;
    use surrealdb::sql::Thing;
    use crate::qtrade::strategy::upstop_return::UpstopReturnStrategyBuyWorker;
    use crate::todo::MAX_KLINE_DAYS;
    use chrono::Utc;
    use chrono::Duration;
    use chrono::TimeZone;

    #[tokio::test]
    async fn test_selected_active_securities() {
        let _ = db::init().await.unwrap();
        pretty_env_logger::init_timed();

        let klts = Utc.with_ymd_and_hms(2024, 9, 10, 0, 0, 0).unwrap();

        let mut klsums = db::kline
            ::select_summary(
                Some(klts),
                Some(Market::SH),
                Some(security::SecurityType::STK),
                None,
                Some("klts DESC"),
                0,
                320 // 320 securities' kline
            ).await
            .unwrap();

        klsums.sort_by(|a, b|
            b
                .upstop_count(3 * 31)
                .partial_cmp(&a.upstop_count(3 * 31))
                .unwrap_or(std::cmp::Ordering::Equal)
                .then_with(||
                    b
                        .upstop_count(6 * 31)
                        .partial_cmp(&a.upstop_count(6 * 31))
                        .unwrap_or(std::cmp::Ordering::Equal)
                )
                .then_with(||
                    b
                        .upstop_count(12 * 31)
                        .partial_cmp(&a.upstop_count(12 * 31))
                        .unwrap_or(std::cmp::Ordering::Equal)
                )
                .then_with(||
                    b.smpr(21).partial_cmp(&a.smpr(21)).unwrap_or(std::cmp::Ordering::Equal)
                )
                .then_with(||
                    b.body_ratio().partial_cmp(&a.body_ratio()).unwrap_or(std::cmp::Ordering::Equal)
                )
        );

        for (idx, klsum) in klsums.iter().enumerate() {
            if idx < 100 || idx > klsums.len() - 10 {
                // klsum.lowest_low_dev(3 * 31) > 0 &&
                println!(
                    "[{idx}]{:?} usc-3:{:02} 6:{:02} 12:{:02} 36:{:02} lld-3:{:02}/{:03} 6:{:02}/{:03} 12:{:02}/{:03} 24:{:02}/{:03} 36:{:02}/{:03} lwr(3)={:02} smpr(21)={:02.02} bdr={:02.02} hcd(3)={:02}/{:02}",
                    klsum.id_raw(),

                    klsum.upstop_count(3 * 31),
                    klsum.upstop_count(6 * 31),
                    klsum.upstop_count(12 * 31),
                    klsum.upstop_count(36 * 31),

                    klsum.lowest_low_dev(3 * 31),
                    klsum.lowest_day_dev(3 * 31),

                    klsum.lowest_low_dev(6 * 31),
                    klsum.lowest_day_dev(6 * 31),

                    klsum.lowest_low_dev(12 * 31),
                    klsum.lowest_day_dev(12 * 31),

                    klsum.lowest_low_dev(24 * 31),
                    klsum.lowest_day_dev(24 * 31),

                    klsum.lowest_low_dev(36 * 31),
                    klsum.lowest_day_dev(36 * 31),

                    klsum.lowest_wick_ratio(3 * 31),
                    klsum.smpr(21),
                    klsum.body_ratio(),
                    klsum.highest_close_dev(3 * 31),
                    klsum.highest_day_dev(3 * 31)
                );
            } else {
                if idx == 10 || idx == klsums.len() - 10 {
                    //
                    println!("");
                } else {
                    print!(".");
                }
            }
        }
    }

    #[tokio::test]
    async fn test_sort_selected_kline_summary_by_security() {
        let _ = db::init().await.unwrap();
        pretty_env_logger::init_timed();
        let mut klsums = db::kline
            ::select_summary(
                None,
                None,
                None,
                Some("SH.STK.600195"),
                Some("klts DESC"),
                0,
                250 //获取最近1年
            ).await
            .unwrap();
        klsums.reverse();

        for (idx, klsum) in klsums.iter().enumerate() {
            if idx < 10 || idx > klsums.len() - 10 {
                // klsum.lowest_low_dev(3 * 31) > 0 &&
                println!(
                    "[{idx}]{:?} lld-3:{:02}/{:03} 6:{:02}/{:03} 12:{:02}/{:03} 24:{:02}/{:03} 36:{:02}/{:03} usc-3:{:02} 6:{:02} 12:{:02} 36:{:02} lwr(3)={:02} smpr(21)={:02.02} bdr={:02.02} hcd(3)={:02}/{:02}",
                    klsum.id_raw(),

                    klsum.lowest_low_dev(3 * 31),
                    klsum.lowest_day_dev(3 * 31),

                    klsum.lowest_low_dev(6 * 31),
                    klsum.lowest_day_dev(6 * 31),

                    klsum.lowest_low_dev(12 * 31),
                    klsum.lowest_day_dev(12 * 31),

                    klsum.lowest_low_dev(24 * 31),
                    klsum.lowest_day_dev(24 * 31),

                    klsum.lowest_low_dev(36 * 31),
                    klsum.lowest_day_dev(36 * 31),

                    klsum.upstop_count(3 * 31),
                    klsum.upstop_count(6 * 31),
                    klsum.upstop_count(12 * 31),
                    klsum.upstop_count(36 * 31),
                    klsum.lowest_wick_ratio(3 * 31),
                    klsum.smpr(21),
                    klsum.body_ratio(),
                    klsum.highest_close_dev(3 * 31),
                    klsum.highest_day_dev(3 * 31)
                );
            } else {
                if idx == 10 || idx == klsums.len() - 10 {
                    //
                    println!("");
                } else {
                    print!(".");
                }
            }
        }
    }

    #[tokio::test]
    async fn test_sort_selected_kline_summary_by_klts_for_many_securities() {
        let _ = db::init().await.unwrap();
        pretty_env_logger::init_timed();
        let mut klts = Utc.with_ymd_and_hms(2024, 1, 1, 0, 0, 0).unwrap();
        let now = Utc::now();
        let days = now.signed_duration_since(klts).num_days();
        for i in 0..days {
            klts = klts + Duration::days(1);
            let mut klsums = db::kline
                ::select_summary(
                    Some(klts),
                    Some(Market::SH),
                    Some(security::SecurityType::STK),
                    None,
                    Some("klts ASC"),
                    0,
                    3000
                ).await
                .unwrap();

            klsums.sort_by(|a, b|
                a
                    .lowest_low_dev(3 * 31)
                    .partial_cmp(&b.lowest_low_dev(3 * 31))
                    .unwrap_or(std::cmp::Ordering::Equal)
                    .then_with(||
                        b
                            .upstop_count(3 * 31)
                            .partial_cmp(&a.upstop_count(3 * 31))
                            .unwrap_or(std::cmp::Ordering::Equal)
                    )
                    .then_with(||
                        b
                            .lowest_wick_ratio(3 * 31)
                            .partial_cmp(&a.lowest_wick_ratio(3 * 31))
                            .unwrap_or(std::cmp::Ordering::Equal)
                    )
                    .then_with(||
                        b.smpr(21).partial_cmp(&a.smpr(21)).unwrap_or(std::cmp::Ordering::Equal)
                    )
                    .then_with(||
                        b
                            .body_ratio()
                            .partial_cmp(&a.body_ratio())
                            .unwrap_or(std::cmp::Ordering::Equal)
                    )
            );

            println!("\n ~~~~~~{:?}~~~~~~~~~", klts);

            let mut count = 0;
            for (idx, klsum) in klsums.iter().enumerate() {
                if klsum.lowest_low_dev(3 * 31) > 0 && (count < 10 || idx > klsums.len() - 10) {
                    count += 1;
                    println!(
                        "[{idx}]{:?} lld-3:{:02}/{:03} 6:{:02}/{:03} 12:{:02}/{:03} 24:{:02}/{:03} 36:{:02}/{:03} usc-3:{:02} 6:{:02} 12:{:02} 36:{:02} lwr(3)={:02} smpr(21)={:02.02} bdr={:02.02} hcd(3)={:02}/{:02}",
                        klsum.id_raw(),

                        klsum.lowest_low_dev(3 * 31),
                        klsum.lowest_day_dev(3 * 31),

                        klsum.lowest_low_dev(6 * 31),
                        klsum.lowest_day_dev(6 * 31),

                        klsum.lowest_low_dev(12 * 31),
                        klsum.lowest_day_dev(12 * 31),

                        klsum.lowest_low_dev(24 * 31),
                        klsum.lowest_day_dev(24 * 31),

                        klsum.lowest_low_dev(36 * 31),
                        klsum.lowest_day_dev(36 * 31),

                        klsum.upstop_count(3 * 31),
                        klsum.upstop_count(6 * 31),
                        klsum.upstop_count(12 * 31),
                        klsum.upstop_count(36 * 31),
                        klsum.lowest_wick_ratio(3 * 31),
                        klsum.smpr(21),
                        klsum.body_ratio(),
                        klsum.highest_close_dev(3 * 31),
                        klsum.highest_day_dev(3 * 31)
                    );
                } else {
                    if count == 10 || idx == klsums.len() - 10 {
                        //
                        println!("");
                    } else {
                        print!(".");
                    }
                }
            }

            let _rs = std::io::stdout().flush(); // Flush stdout to ensure immediate output
        }
    }

    #[tokio::test]
    async fn test_compute_klines_summary() {
        let _ = db::init().await.unwrap();
        pretty_env_logger::init_timed();

        let securities = if true {
            db::security
                ::select(
                    &Some(Market::SH), //&None, //
                    Some(security::SecurityType::STK),
                    None,
                    Some("id ASC"),
                    1,
                    160 //MAX_STK_SECURITY
                ).await
                .unwrap()
        } else {
            vec![Security {
                // id: Some(Thing::from(("security", "SZ.STK.301292"))),
                id: Some(Thing::from(("security", "SH.STK.600769"))),
                ..Default::default()
            }]
        };

        for (idx, security) in securities.iter().enumerate() {
            let security_id = security.id_raw();

            let rs = UpstopReturnStrategyBuyWorker::new(security_id.as_str(), &Klt::DAY).await;

            match rs {
                Ok(mut worker) => {
                    let klsums = worker.compute_klines_summary(
                        &vec![
                            3 * 31 // 计算当日开始3个月内区间的
                            // (0, 6 * 31),
                            // (0, 12 * 31),
                            // (0, 24 * 31),
                            // (0, 36 * 31) // 计算当日开始3年内区间的
                        ],
                        MAX_KLINE_DAYS as usize // 100
                    );
                    let _rs = db::kline::upsert_all_klines_stat(&klsums).await;
                    info!(
                        "test_compute_klines_summary [{idx}/{}] {} updated={:?}",
                        securities.len(),
                        security_id,
                        _rs
                    );
                }

                Err(e) => {
                    error!(
                        "test_compute_klines_summary [{idx}/{}] {} err={:?}",
                        securities.len(),
                        security_id,
                        e
                    );
                }
            }

            // let _rs = std::io::stdout().flush(); // Flush stdout to ensure immediate output
        }
    }

    #[tokio::test]
    async fn test_compute_stbuys() {
        let _ = db::init().await.unwrap();

        // let mut all_buys = Vec::new();

        let securities;
        if true {
            securities = db::security
                ::select(
                    &Some(Market::SH), //&None, //
                    Some(security::SecurityType::STK),
                    None,
                    Some("id ASC"),
                    0,
                    10000 //MAX_STK_SECURITY
                ).await
                .unwrap();
        } else {
            securities = vec![Security {
                // id: Some(Thing::from(("security", "SZ.STK.301292"))),
                id: Some(Thing::from(("security", "SH.STK.600769"))),
                ..Default::default()
            }];
        }

        for security in securities.iter() {
            let security_id = security.id.clone().unwrap().id.to_raw();

            let rs = UpstopReturnStrategyBuyWorker::new(security_id.as_str(), &Klt::DAY).await;

            match rs {
                Ok(mut worker) => {
                    let mut stbuys = worker.compute_stbuys();
                    println!("\n {}***************", security_id);
                    for (idx, trade_stbuy) in stbuys.iter().enumerate() {
                        // match &trade_stbuy.data {
                        //     Some(TradeStbuyData::UpstopReturnBuy(stbuy)) => {
                        //         // Process upstop return buy data
                        //         println!("STBUY@[{idx}]{:?}, {:?}", trade_stbuy.id, stbuy);
                        //     }
                        //     _ => {
                        //         // Handle the case where data is None
                        //         println!("\nIGNORE@[{idx}]{:?}, ", trade_stbuy.id);
                        //     }
                        // }
                    }

                    // all_buys.extend(buys);

                    let mut total_profit = 0.0;
                    for (idx, stbuy) in stbuys.iter_mut().enumerate() {
                        println!("\n{idx:02}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
                        total_profit += compute_profit(&worker, stbuy);
                    }
                    println!("\n total_profit= {total_profit}\n");
                }

                Err(e) => {
                    println!("err={:?}", e);
                }
            }

            let _rs = std::io::stdout().flush(); // Flush stdout to ensure immediate output
        }
    }

    fn compute_profit(worker: &UpstopReturnStrategyBuyWorker, stbuy: &mut ValleyReboundBuy) -> f64 {
        println!("STBUY@{:?}", stbuy);
        let torders = worker.compute_trade_orders(stbuy);
        println!("STBUY@{:?}", stbuy);
        for (idx, torder) in torders.iter().enumerate() {
            println!("\n[{:02}] {:?}", idx, torder);
        }
        println!("\n");
        let mut total_profit = 0.0;
        let mut total_cost = 0.0;
        for (idx, torder) in torders.iter().enumerate() {
            match &torder.data {
                Some(TradeOrderData::Buy(b)) => {
                    println!(
                        "[{:02}] {:?} B@{} spl:{}",
                        idx,
                        torder.kline_id,
                        b.buy_price,
                        b.spl_price
                    );
                    let amount = b.buy_price * (b.volume as f64);
                    total_profit -= amount;
                    total_cost += amount * 1.001;
                }
                Some(TradeOrderData::Sell(s)) => {
                    println!("[{:02}] {:?} S@{}", idx, torder.kline_id, s.price);
                    let amount = s.price * (s.volume as f64);
                    total_profit += amount;
                    total_cost += amount * 0.001;
                }
                None => {}
            }
        }
        let profit_ratio = round2((total_profit / total_cost) * 100.0);
        println!(
            "Total:[00] {:?} A@{}/{}={}%",
            "SZ.STK.301292.DAY.yyyymmdd".to_string(),
            total_profit,
            total_cost,
            profit_ratio
        );
        profit_ratio
    }
}
